package com.ynet.cat.rpc;

/**
 * 项目名：ifp-contract <br>
 * 包名：com.ynet.middleground.contract.filter <br>
 * 类名：CatDubboProviderFilter <br>
 * 描述：
 *
 * @author Suremotoo
 * @create 2022-07-07 17:54
 */

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;
import org.slf4j.MDC;

import com.alibaba.fastjson.JSON;
import com.dianping.cat.Cat;
import com.dianping.cat.message.Transaction;
import com.google.common.base.Throwables;
import com.ynet.core.common.BaseReqObj;
import com.ynet.core.log.IfpLogger;
import com.ynet.core.util.IDUtil;
import com.ynet.middleground.contract.filter.SpecialLogFilter;

@Activate(group = {"provider"}, order = -20000)
public class CatDubboProviderFilter implements Filter {

    private static final String CAT_RPC_TYPE_SERVER = "DUBBO-SERVER";
    private static final String REST_SWAGGER_URL =
        "org.apache.dubbo.rpc.protocol.rest.integration.swagger.DubboSwaggerService";
    private static String TRACE_KEY = "traceId";
    private static String TRACE_CHANNEL_KEY = "channel";
    private static String TRACE_USER_ID_KEY = "logUserId";
    private static String LOG_ID_KEY = "logId";

    public CatDubboProviderFilter() {}

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        IfpLogger.debug("CatDubboProviderFilter", "提供者Cat链路跟踪Filter", new Object[0]);
        Object[] args = invocation.getArguments();
        String methodSignature = this.buildMethodName(invoker, invocation) + this.buildMethodArguments(invocation);
        String returnResult = "";
        this.setAttachmentFromBaseReqObj(args);
        if (methodSignature.startsWith("org.apache.dubbo.rpc.protocol.rest.integration.swagger.DubboSwaggerService")) {
            return invoker.invoke(invocation);
        } else {
            Long startTime = System.currentTimeMillis();
            Long takeTime = 0L;
            String interfaceName = invoker.getInterface().getName();
            Transaction transaction = Cat.newTransaction("DUBBO-SERVER", this.buildMethodName(invoker, invocation));
            String rootId = invocation.getAttachment("_catRootMessageId");
            String parentId = invocation.getAttachment("_catParentMessageId");
            String childId = invocation.getAttachment("_catChildMessageId");
            if (rootId != null) {
                RpcCatContext ctx = new RpcCatContext(rootId, parentId, childId);
                Cat.logRemoteCallServer(ctx);
            }

            transaction.addData("method", methodSignature);

            Result var14;
            try {
                Result result = invoker.invoke(invocation);
                if (null != result && result.hasException()) {
                    transaction.setStatus(result.getException());
                } else {
                    transaction.setStatus("0");
                }

                takeTime = System.currentTimeMillis() - startTime;
                returnResult = null != result ? JSON.toJSONString(result.getValue()) : "";
                var14 = result;
            } catch (RpcException var18) {
                IfpLogger.error("DubboFilter", "request{},Exception:{},",
                    new Object[] {invocation.getClass(), Throwables.getStackTraceAsString(var18)});
                transaction.setStatus(var18);
                throw var18;
            } finally {
                /*
                * 修改说明：
                * 由于接口会涉及文件流 fileBase64 字符串大报文，打印日志会导致 logstash 崩掉，当前应用无法将后续日志继续
                * 推送至 logstash，造成内存无法释放，进一步导致当前应用 OOM
                * 故，调整当前过滤器日志不推送至 logstash，只打印到日志文件 
                * author: modify by liulx at 2022-07-07 19:57:35
                */
                IfpLogger.info(SpecialLogFilter.FILTER_FOR_LOGSTASH_PREFIX + " CatDubboProviderFilter ",
                    "method:[{}], request:{}, response:{}, takeTime:{} ms",
                    new Object[] {methodSignature, invocation.getArguments(), returnResult, takeTime});
                MDC.clear();
                transaction.complete();
            }

            return var14;
        }
    }

    private void setAttachmentFromBaseReqObj(Object[] args) {
        String traceId = "";
        String channel = "";
        Integer logUserId = 0;
        Object[] var5 = args;
        int var6 = args.length;

        for (int var7 = 0; var7 < var6; ++var7) {
            Object arg = var5[var7];
            if (null != arg && arg instanceof BaseReqObj) {
                traceId = ((BaseReqObj)arg).getTraceId();
                channel = ((BaseReqObj)arg).getChannel();
                logUserId = ((BaseReqObj)arg).getOperationUserId();
                break;
            }
        }

        String generateTraceId = this.generateTraceId();
        RpcContext.getContext().setAttachment(TRACE_KEY, traceId);
        RpcContext.getContext().setAttachment(TRACE_CHANNEL_KEY, channel);
        RpcContext.getContext().setAttachment(TRACE_USER_ID_KEY, logUserId + "");
        RpcContext.getContext().setAttachment(LOG_ID_KEY, generateTraceId);
        MDC.put(TRACE_KEY, traceId);
        MDC.put(LOG_ID_KEY, generateTraceId);
        MDC.put(TRACE_CHANNEL_KEY, channel);
        MDC.put(TRACE_USER_ID_KEY, logUserId + "");
    }

    private String generateTraceId() {
        return " - " + IDUtil.getSecureRandomId();
    }

    private String buildMethodName(Invoker<?> invoker, Invocation invocation) {
        StringBuilder methodNameBuilder = new StringBuilder();
        methodNameBuilder.append(invoker.getInterface().getName()).append(".").append(invocation.getMethodName());
        return methodNameBuilder.toString();
    }

    private String buildMethodArguments(Invocation invocation) {
        StringBuilder methodArgumentsBuilder = new StringBuilder("(");
        Object[] args = invocation.getArguments();
        if (args != null && args.length > 0) {
            Object[] var4 = args;
            int var5 = args.length;

            for (int var6 = 0; var6 < var5; ++var6) {
                Object arg = var4[var6];
                methodArgumentsBuilder.append(JSON.toJSONString(arg)).append(", ");
            }

            int length = methodArgumentsBuilder.length();
            methodArgumentsBuilder = methodArgumentsBuilder.replace(length - 2, length, "");
        }

        return methodArgumentsBuilder.append(")").toString();
    }

}
